ReentrantLock基于AQS实现的一种独占锁,也是重入锁。它实现了非公平锁和公平锁。本篇我们将通过源码对此做分析。
继承关系和构造
1 | public class ReentrantLock implements Lock, java.io.Serializable { |
ReentrantLock结构非常简单,它实现了Lock和Serializable接口,Lock接口包含了实现锁的基本方法,比如lock,unlock等。同时,ReentrantLock包含一个Sync对象,这里的Sync实际上为AQS的一种实现模型,关于AQS我们之前有所介绍,它是一种并发同步框架,ReentrantLock的两个模式的锁都是基于Sync实现的。
Sync同步模型
1 | abstract static class Sync extends AbstractQueuedSynchronizer { |
Sync实现了继承自AQS框架模型,它为子类提供了lock抽象方法。同时默认实现了nonfairTryAcquire即非公平锁的实现,以及tryRelease方法即锁的释放。ReentrantLock基于Sync实现了非公平和公平锁,默认是非公共锁。
1 | public ReentrantLock() { |
###非公平锁
1 | static final class NonfairSync extends Sync { |
基于Sync,NonfairSync实现了lock抽线方法,并且同时实现AQS的tryAcquire方法,这里我们看看非公平锁的实现。
非公平锁获取锁的实现:
- 通过CAS尝试获取锁,如果成功,当前线程将取得执行权
- 否则通过AQS的acquire来获取锁,最终通过tryAquire来获取锁,这里通过Sync的nonfairTryAcquire方法实现。
1 | final boolean nonfairTryAcquire(int acquires) { |
在nonfairTryAcquire方法中,通过getState获取锁状态,如果为0,则表示当前锁处于空闲状态,尝试通过CAS获取锁,如果成功,则获取执行权。否则判断当前线程是否已经执行,如果已经执行的话就表示线程已经获取到了锁,需要重入,这里对锁状态加1,否则就返回false交给AQS处理,即将当前线程封装为Node节点添加到FIFO队列中,然后park线程。
公平锁
1 | static final class FairSync extends Sync { |
公平锁的lock实现是通过其tryAcquire方法实现,它的实现原理如下:
- 首先获取获取锁的状态,如果为0表示锁空闲,则再判断是否队列中还有等待的前继线程,如果没有的话尝试通过CAS获取锁,如果成功,线程获取执行权,否则返回false,将线程添加到等待队列。否则判断当前线程是否已经执行,如果是,则表示重入,需要对锁状态+1。
区别
公平锁和非公平锁的区别在于:
-
非公平锁再lock时不管当前锁的状态,直接尝试通过CAS获取锁,而非公共锁在lock时首先判断当前锁状态,如果已经被占有,就直接去队列中等待了。
-
在非公平锁和公平锁中如果都检测到锁空闲,非公平锁直接也是通过CAS尝试获取锁,而公平锁会去检查等待队列是否有线程在等待,如果没有才尝试通过CAS获取锁,否则直接添加到等待队列中。
可见非公平锁和公平锁的最大区别都在于,非公平锁的实现常常显的粗暴,有点类似于插队,即它不管当前队列和锁的情况先直接去插队(尝试获取锁),成功了就直接去执行了,失败了就再到队列中排队,而公平锁显的温和许多,先看看队列或者锁是否都在忙,如果是就直接去队列中排队了。